#include "stdafx.h"
#undef UNICODE
#include <Windows.h>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <mcam_zei.h>
#include <math.h>
#include <tiff.h>
#include <tiffio.h>
#include <tiffconf.h>
#include <tif_config.h>

#include "defines.h"
#include "vision.h"
#include "tiffoperations.h"

using namespace std;
using namespace cv;

//camera thread for live imaging, does not work properly yet...
//the reason it doesnt work properly is because of the usage of OpenCV, we'd have to convert the
//live image to openCV constantly, which causes a great amount of stuttering in that window
//and can potentially lead to a crash, would be better to somehow use MFC commands for this
//little something for future implementations in a GUI?

//DWORD WINAPI openCamera(LPVOID lpParam)
//{
//	HMODULE m_hLibHR3 = LoadLibrary( "hrfw.dll" );
//	unsigned short* imageData;
//	long allocatedSize;
//	long alignwidth, width, height, widthbytes;
//	long err = 0;
//	Mat temp, image, final;
//	SMCAMINFO info;
//	double resizeScale;
//
//	// Acquisition
//	long (WINAPI *p_McammAcquisition)(long cameraindex, unsigned short* Ptr, long Size, McamImageProcEx CB );
//	long (WINAPI *p_McammAcquisitionEx)(long cameraindex, unsigned short* Ptr, long Size, McamImageProcEx CB, void* UserParam );
//	long (WINAPI *p_McammStartFastAcquisition)(long cameraindex);
//	long (WINAPI *p_McammAbortFastAcquisition)(long cameraindex);
//	long (WINAPI *p_McammIsFastAcquisitionReady)( long cameraindex, unsigned short* pImageData, long allocatedSize, BOOL bStartNext );
//	long (WINAPI *p_McammGetCurrentDataSize)( long cameraindex, long* pWidth, long* pHeight);
//
//	p_McammAcquisitionEx				= (long (WINAPI*)(long,unsigned short *, long, int (__cdecl *)(long,long,eMcamStatus,void*), void*))GetProcAddress( m_hLibHR3, "_McammAcquisitionEx@20" );
//	p_McammStartFastAcquisition			= (long (WINAPI*)(long))GetProcAddress( m_hLibHR3, "_McammStartFastAcquisition@4" );
//	p_McammAbortFastAcquisition			= (long (WINAPI*)(long))GetProcAddress( m_hLibHR3, "_McammAbortFastAcquisition@4" );
//	p_McammIsFastAcquisitionReady		= (long (WINAPI*)(long,unsigned short *,long,int))GetProcAddress( m_hLibHR3, "_McammIsFastAcquisitionReady@16" );
//	p_McammGetCurrentDataSize			= (long (WINAPI*)(long,long*,long*))GetProcAddress( m_hLibHR3, "_McammGetCurrentDataSize@12" );
//
//	void (WINAPI *p_McammClose)( long cameraindex );
//	p_McammClose	= ( void(WINAPI*)(long) )GetProcAddress(m_hLibHR3, "_McammClose@4" );
//
////	resizeScale = calcScaling(20.0, TEMPL5, 5);
//	(*p_McammGetCurrentDataSize)(0, &width, &height);
//	alignwidth = ((width * 3) + 0x0001) & 0xFFFE;
//	allocatedSize = 2 * alignwidth * height;
//	imageData = new unsigned short[alignwidth*height];
//	namedWindow( "debugcap", CV_WINDOW_AUTOSIZE );
//	while(1){
//		if (!cvGetWindowHandle("debugcap"))
//		{
//			(*p_McammAcquisitionEx)(0, imageData, allocatedSize, NULL, NULL);
//			image = Mat(height, width, CV_16UC3, imageData, Mat::AUTO_STEP);
//			resize(image, image, Size(), resizeScale, resizeScale, INTER_AREA);
//			//imshow( "debugcap", image );
//			break;
//		}
//		(*p_McammAcquisitionEx)(0, imageData, allocatedSize, NULL, NULL);
//		image = Mat(height, width, CV_16UC3, imageData, Mat::AUTO_STEP);
//		resize(image, image, Size(), resizeScale, resizeScale, INTER_AREA);
//		imshow( "debugcap", image );
//		cvWaitKey(2);
//	}
//	(*p_McammClose)(0);
//	ExitThread(0);
//	return 0;
//}

Mat acquireImage(HMODULE m_hLibHR3)
{
	unsigned short* imageData;
	long allocatedSize;
	long alignwidth, width, height;
	Mat temp, image, final;
//	SMCAMINFO info;

	//declare functions from DLL to acquire images and information form current image
	long (WINAPI *p_McammGetCurrentDataSize)( long cameraindex, long* pWidth, long* pHeight);
	p_McammGetCurrentDataSize			= (long (WINAPI*)(long,long*,long*))GetProcAddress( m_hLibHR3, "_McammGetCurrentDataSize@12" );
	long (WINAPI *p_McammAcquisitionEx)(long cameraindex, unsigned short* Ptr, long Size, McamImageProcEx CB, void* UserParam );
	p_McammAcquisitionEx	= (long (WINAPI*)(long,unsigned short *, long, int (__cdecl *)(long,long,eMcamStatus,void*), void*))GetProcAddress( m_hLibHR3, "_McammAcquisitionEx@20" );

	//acquire current datasize of current screen from camera
	(*p_McammGetCurrentDataSize)(0, &width, &height);

	//cout << width << " " << height << endl; //just to prove we can get width and height!

	//this is proposed in the API of Zeiss, used for allocating memory for camera capture
	alignwidth = ((width * 3) + 0x0001) & 0xFFFE;
	//how many bytes do we have to allocate
	allocatedSize = 2 * alignwidth * height;
	//create an imagedata based on the amount of bytes needed
	imageData = new unsigned short[alignwidth*height];
	//acquire a snapshot from the camera and write it to the above created buffer
	(*p_McammAcquisitionEx)(0, imageData, allocatedSize, NULL, NULL);
	
	//multiply all data in the buffer by 4 to scale the image to 16 bit, which is needed for OpenCV
	for(int i=0;i<alignwidth*height;i++)
	{
		imageData[i]=imageData[i]*4;
	}
	//create an OpenCV Mat databuffer and fill it with the acquired imagedata
	temp = Mat(height, width, CV_16UC3, imageData, Mat::AUTO_STEP);
	
	cvtColor(temp, image, CV_8U, 3); //to ensure the correct colour scheme being used
	image.convertTo(final, CV_8U, 0.00390625); //to ensure the conversion of depth, 16bit to 8bit = 1/256=0.00390625

	//return the template to main
	return final;
}

/*
 * This routine is used for template matching between the used tiled pyramid bigtiff file and a snapshot (template)
 * created at a magnification of 5x and a resolution of 1388x1044 with the AxioPlan 2 microscope.
 */
Point matchTemplRoutine(Mat rgbImage, Mat templateImg)
{
	Mat result;
	double minVal = 0; 
	double maxVal = 0; 
	Point minLoc; 
	Point maxLoc;
	Point matchLoc;

	int match_method = TM_CCOEFF_NORMED;

	matchTemplate(rgbImage, templateImg, result, match_method);

	normalize(result, result, 0, 1, NORM_MINMAX, -1, Mat());

	minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, Mat());

	//For SQDIFF and SQDIFF_NORMED, the best matches are lower values. For all the other methods, the higher the better
	if( match_method  == CV_TM_SQDIFF || match_method == CV_TM_SQDIFF_NORMED )
		{ matchLoc = minLoc; }
	else
		{ matchLoc = maxLoc; }

	rectangle(rgbImage, matchLoc, Point(matchLoc.x + templateImg.cols , matchLoc.y + templateImg.rows), Scalar::all(0), 1, 8, 0);

	return matchLoc;
}
